import { forkHandler, mergeState } from '@/utils';
import React, { Children, cloneElement, useContext, useEffect, useRef, useState } from 'react';
import { useCall } from '../';
import { Context, RowCtx } from './shared';

function DragHandle({ children, component: Comp = 'span', ...props }) {
  const rowCtx = useContext(RowCtx);
  const ctx = useContext(Context);
  const onDragBind = useRef(ctx.onDrag.bind(null, rowCtx)).current;

  const onMouseDown = useCall(forkHandler(props.onMouseDown, onDragBind, true));
  const onPointerDown = useCall(forkHandler(props.onPointerDown, onDragBind, true));
  const onTouchStart = useCall(forkHandler(props.onTouchStart, onDragBind, true));

  const [cursor, setCursor] = useState('grab');

  useEffect(() => {
    return ctx.subscribe((draggingRowCtx) => {
      setCursor(ctx.getRowKey(draggingRowCtx) === ctx.getRowKey(rowCtx) ? 'grabbing' : 'grab');
    });
  }, []);

  const style = {
    ...props.style,
    touchAction: 'none',
    cursor,
  };

  const mergeProps = {
    style,
    onMouseDown,
    onPointerDown,
    onTouchStart,
  };

  if (Children.count(children) === 1) {
    const mergeProp = (name, value) => {
      const prop = children.props[name];
      switch (typeof value) {
        case 'function':
          return children.props[name] ? forkHandler(prop, value) : value;

        case 'object':
          return mergeState({ ...prop }, value);

        default:
          return value;
      }
    };
    return cloneElement(
      children,
      Object.keys(mergeProps).reduce((ret, key) => ((ret[key] = mergeProp(key, mergeProps[key])), ret), {}),
    );
  }

  return (
    <Comp {...props} {...mergeProps}>
      {children}
    </Comp>
  );
}

export default DragHandle;
